package com.seal.commons.infrastructure.redis.distributed.lock;

import com.seal.commons.infrastructure.facade.distributed.lock.AbstractDistributedLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * ClassName:RedisDistributedLock <br/>
 * Function:  <br/>
 * Date:     2019-12-21 14:35 <br/>
 *
 * @author yong.huang
 * @see
 * @since JDK 1.8
 */
@Slf4j
public class RedisDistributedLock extends AbstractDistributedLock {

    RedissonClient redissonClient;

    public RedisDistributedLock(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Override
    public boolean lock(String key, long expire, int retryTimes, long sleepMillis) {
        RLock lock = redissonClient.getLock(key);
        boolean  isLock = false;
        while ((!isLock) && retryTimes-- > 0 ) {
            try {
                isLock = lock.tryLock(sleepMillis, expire, TimeUnit.MILLISECONDS);
                if (isLock) {
                    return true;
                }
            } catch (InterruptedException e) {
                log.error("get lock error, key:[{}],retryTimes:[{}] ", key, retryTimes);
            }
        }
        return isLock;
    }

    @Override
    public boolean releaseLock(String key) {
        RLock lock = redissonClient.getLock(key);
        lock.unlock();
        return true;
    }

    @Override
    public <T> T acquire(String key, long expire, int retryTimes, long sleepMillis, Supplier<T> supplier) {
        RLock lock = redissonClient.getLock(key);
        T result = null;
        boolean isLock = false;
        while ((!isLock) && retryTimes-- > 0 ) {
            try {
                isLock = lock.tryLock(sleepMillis, expire, TimeUnit.MILLISECONDS);
                log.info("redis tryLock success, key:[{}]", key);
                if (isLock) {
                    try{
                        result = supplier.get();
                    }finally {
                        lock.unlock();
                        log.info("redis lock unlock, key:[{}]", key);
                    }
                }
            } catch (InterruptedException e) {
                log.error("get lock error, key:[{}],retryTimes:[{}] ", key, retryTimes);
            }
        }
        return result;
    }
}
